package com.ybear.mvp.presenter;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.util.Printer;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;

import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.util.Consumer;

import com.ybear.mvp.ViewLife;
import com.ybear.mvp.annotations.LifeStatus;
import com.ybear.mvp.annotations.Model;
import com.ybear.mvp.handler.Handler;
import com.ybear.mvp.model.ModelManage;
import com.ybear.mvp.util.MvpAnn;
import com.ybear.mvp.view.MvpViewable;

import java.lang.ref.WeakReference;
import java.util.concurrent.Callable;

/**
 * P层
 * @param <V> {@link MvpViewable}
 */
public class MvpPresenter<V extends MvpViewable> implements IMvpPresenter<V> {
    private WeakReference<V> wrfView;       //虚引用V层
    @Model
    private ViewLife<V> mLife;

    private MvpPresenter() { }
    public MvpPresenter(@NonNull V view) {
        //实例被Model注解的成员变量
        MvpAnn.instanceMvpAnn( this );
        //虚引用V层
        wrfView = new WeakReference<>( view );
        V v = getView();
        //注册监听Life
        if( v != null ) mLife.registerPresenter( v, this );
    }

    /**
     * 释放P层
     * 当V层销毁时会被调用，此时getView会为null，可以在onActiveStatus中处理销毁后的操作
     */
    private void release() {
        onDestroyPresenter();
        V v = getView();
        //解除监听Life
        if( v != null ) mLife.unRegisterPresenter( v );
        //移除Presenter存在的所有Model
        ModelManage.get().removeModelAll( this );
        //释放V层接口
        if( wrfView != null ) {
            wrfView.clear();
            wrfView = null;
        }
    }

    public void onDestroyPresenter() {}

    /**
     * 获取V层
     * @return  返回V层
     */
    @Nullable
    @Override
    public V getView() { return wrfView != null ? wrfView.get() : null; }

    @NonNull
    @Override
    public V getViewOrThrow() throws NullPointerException {
        V v = getView();
        if( v != null ) return v;
        throw new NullPointerException( "getView() is null!" );
    }

    /**
     * 获取V层
     * @param call          返回V层
     * @param h             Handler
     * @param duration      延迟毫秒
     */
    @Override
    public void viewCall(@NonNull Call<V> call, @Nullable Handler h, int duration) {
        if( h == null ) {
            V view = getView();
            if( view != null ) call.onCall( view );
            return;
        }
        h.post( ()-> {
            V view = getView();
            if( view != null ) call.onCall( view );
        }, duration);
    }

    /**
     * 获取V层
     * @param call          返回V层
     * @param h             Handler
     */
    @Override
    public void viewCall(@NonNull Call<V> call, @Nullable Handler h) {
        viewCall( call, h, 0 );
    }

    /**
     * 获取V层
     * @param call          返回V层
     * @param duration      延迟毫秒
     */
    @Override
    public void viewCall(@NonNull Call<V> call, int duration) {
        viewCall( call, this, duration );
    }

    /**
     * 获取V层
     * @param call  返回V层
     */
    @Override
    public void viewCall(@NonNull Call<V> call) { viewCall( call, this ); }

    /**
     * 获取V层
     * @param call  返回V层
     */
    @Override
    public void viewCallNotHandler(@NonNull Call<V> call) { viewCall( call, null ); }

    @Override
    public MvpPresenter<V> getThis() { return this; }

    @Override
    public Context getApplicationContext() {
        V v = getView();
        return v != null ? v.getApplicationContext() : null;
    }

    /**
     * 获取上下文
     * @return  上下文
     */
    @Nullable
    @Override
    public Context getContext() {
        V v = getView();
        return v != null ? v.getContext() : null;
    }

    /**
     * 获取Activity
     * @return  页面的this或getActivity()
     */
    @Nullable
    @Override
    public Activity getActivity() {
        V v = getView();
        return v != null ? v.getActivity() : null;
    }

    @Nullable
    @Override
    public Resources getResources() {
        V v = getView();
        return v != null ? v.getResources() : null;
    }

    /**
     * 查找View
     * @param id        资源id
     * @param <T>       返回的View
     * @return          View
     */
    @Nullable
    @Override
    public final <T extends View> T findViewById(@IdRes int id) {
        V v = getView();
        return v != null ? v.findViewById( id ) : null;
    }

    @Nullable
    @Override
    public Window getWindow() {
        V v = getView();
        if( v != null ) return v.getWindow();
        return null;
    }

    /**
     * 关闭页面
     */
    @Override
    public void finish() {
        V v = getView();
        if( v != null ) v.finish();
    }

    /**
     * 获取一个Bundle
     * @return          {@link Bundle}
     */
    public Bundle newBundle() { return mLife.newBundle(); }

//    /**
//     * FragmentActivity传递参数给Fragment
//     * @param fragment  传递的Fragment
//     * @param args      参数
//     */
//    public void setArgumentsToFragment(MvpFragment fragment, @Nullable Bundle args) {
//        mLife.setArgumentsToFragment( fragment, args );
//    }
//
//    /**
//     * FragmentActivity传递参数给Fragment
//     * @param fragment  传递的Fragment
//     * @param args      参数
//     */
//    public void setArgumentsToFragment(com.ybear.mvp.demo.view.MvpFragment fragment,
//                                               @Nullable Bundle args) {
//        mLife.setArgumentsToFragment( fragment, args );
//    }

    /**
     * Fragment传递参数给FragmentActivity
     * @param args      参数
     */
    public void setArgumentsToActivity(@Nullable Bundle args) {
        mLife.setArgumentsToActivity( getView(), args );
    }

    public void onCallArguments(Bundle b) { }

    @LifeStatus
    @Override
    public int getLifeStatus() { return mLife.getLifeStatus(); }

    @Override
    public void onLifeStatus(@LifeStatus int status, @Nullable Bundle b) {
        V v = getView();
        if( v != null ) v.onLifeStatus( status, b );
    }

    @Override
    public void onLifeNewIntent(Intent intent) { }

    @Override
    public void onLifeAttach() {}

    @Override
    public void onLifeCreate(@Nullable Bundle savedInstanceState) {}

    @Override
    public void onLifeCreateView(@Nullable Bundle savedInstanceState) {}

    @Override
    public void onLifeActivityCreate(@Nullable Bundle savedInstanceState) {}

    @Override
    public void onLifeStart() {}

    @Override
    public void onLifeReStart() {}

    @Override
    public void onLifeResume() {}

    @Override
    public void onLifePause() {}

    @Override
    public void onLifeStop() {}

    @Override
    public void onLifeDestroyView() {}

    @Override
    public void onLifeDestroy() { release(); }

    @Override
    public void onLifeDetach() {}

    @Override
    public int getTriggerStatus() { return mLife.getTriggerStatus(); }

    @Override
    public void onTriggerStatus(int trigger, @Nullable Bundle b) {
        mLife.onTriggerStatus( trigger );
    }

    @Override
    public void onTriggerRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) { }

    @Override
    public void onTriggerActivityResult(int requestCode, int resultCode, @Nullable Intent data) { }

    @Override
    public void onTriggerSaveInstanceState(@NonNull Bundle outState) { }

    @Override
    public void onTriggerSaveInstanceState(@NonNull Bundle outState,
                                    @Nullable PersistableBundle outPersistentState) { }

    @Override
    public void onTriggerHiddenChanged(boolean hidden) { }

    @Override
    public void onTouchEvent(MotionEvent ev) { }

    @Override
    public void dispatchTouchEvent(MotionEvent ev) { }

    @Override
    public void dispatchKeyEvent(KeyEvent ev) { }

    @Override
    public void dispatchKeyShortcutEvent(KeyEvent ev) { }

    @Override
    public void dispatchTrackballEvent(MotionEvent ev) { }

    @Override
    public void dispatchGenericMotionEvent(MotionEvent ev) { }

    /* Handler */
    @NonNull
    @Override
    public android.os.Handler getOsHandler() { return getViewOrThrow().getHandler().getOsHandler(); }
    @NonNull
    @Override
    public final Handler getHandler() { return getViewOrThrow().getHandler().getHandler(); }
    @Override
    public boolean post(Runnable r) { return getHandler().post( r ); }
    @Override
    public boolean post(Runnable r, long delayMillis) {
        return getHandler().postDelayed( r, delayMillis );
    }
    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public boolean post(Runnable r, @Nullable Object token, long delayMillis) {
        return getHandler().postDelayed( r, token, delayMillis );
    }
    @Override
    public boolean postDelayed(Runnable r, long delayMillis) {
        return getHandler().postDelayed( r, delayMillis );
    }
    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public boolean postDelayed(@NonNull Runnable r, @Nullable Object token, long delayMillis) {
        return getHandler().postDelayed( r, token, delayMillis );
    }
    @Override
    public boolean postAtFrontOfQueue(@NonNull Runnable r) {
        return getHandler().postAtFrontOfQueue( r );
    }
    @Override
    public boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
        return getHandler().postAtTime( r, uptimeMillis );
    }
    @Override
    public boolean postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
        return getHandler().postAtTime( r, token, uptimeMillis );
    }

    @Override
    public <T> boolean postAsync(Callable<T> executeCall, Consumer<T> resultCall, boolean postResult) {
        return getHandler().postAsync( executeCall, resultCall, postResult );
    }

    @Override
    public <T> boolean postAsync(Consumer<Consumer<T>> executeCall, Consumer<T> resultCall, boolean postResult) {
        return getHandler().postAsync( executeCall, resultCall, postResult );
    }

    @Override
    public <T> boolean postDelayedAsync(Callable<T> executeCall, Consumer<T> resultCall, boolean postResult, long delayMillis) {
        return getHandler().postDelayedAsync( executeCall, resultCall, postResult, delayMillis );
    }

    @Override
    public <T> boolean postDelayedAsync(Consumer<Consumer<T>> executeCall, Consumer<T> resultCall, boolean postResult, long delayMillis) {
        return getHandler().postDelayedAsync( executeCall, resultCall, postResult, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Callable<T> executeCall, Consumer<T> resultCall, boolean postResult, long delayMillis) {
        return getHandler().postAsync( executeCall, resultCall, postResult, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Consumer<Consumer<T>> executeCall, Consumer<T> resultCall, boolean postResult, long delayMillis) {
        return getHandler().postAsync( executeCall, resultCall, postResult, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Callable<T> executeCall, Consumer<T> resultCall) {
        return getHandler().postAsync( executeCall, resultCall );
    }

    @Override
    public <T> boolean postAsync(Consumer<Consumer<T>> executeCall, Consumer<T> resultCall) {
        return getHandler().postAsync( executeCall, resultCall );
    }

    @Override
    public <T> boolean postDelayedAsync(Callable<T> executeCall, Consumer<T> resultCall, long delayMillis) {
        return getHandler().postDelayedAsync( executeCall, resultCall, delayMillis );
    }

    @Override
    public <T> boolean postDelayedAsync(Consumer<Consumer<T>> executeCall, Consumer<T> resultCall, long delayMillis) {
        return getHandler().postDelayedAsync( executeCall, resultCall, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Callable<T> executeCall, Consumer<T> resultCall, long delayMillis) {
        return getHandler().postAsync( executeCall, resultCall, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Consumer<Consumer<T>> executeCall, Consumer<T> resultCall, long delayMillis) {
        return getHandler().postAsync( executeCall, resultCall, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Runnable executeCall) {
        return getHandler().postAsync( executeCall );
    }

    @Override
    public <T> boolean postDelayedAsync(Runnable executeCall, long delayMillis) {
        return getHandler().postDelayedAsync( executeCall, delayMillis );
    }

    @Override
    public <T> boolean postAsync(Runnable executeCall, long delayMillis) {
        return getHandler().postAsync( executeCall, delayMillis );
    }

    @Override
    public boolean sendMessage(@NonNull Message msg) { return getHandler().sendMessage( msg ); }
    @Override
    public boolean sendEmptyMessage(int what) { return getHandler().sendEmptyMessage( what ); }
    @Override
    public boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        return getHandler().sendEmptyMessageDelayed( what, delayMillis );
    }
    @Override
    public boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        return getHandler().sendEmptyMessageAtTime( what, uptimeMillis );
    }
    @Override
    public boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        return getHandler().sendMessageDelayed( msg, delayMillis );
    }
    @Override
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        return getHandler().sendMessageAtTime( msg, uptimeMillis );
    }
    @Override
    public boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        return getHandler().sendMessageAtFrontOfQueue( msg );
    }

    @Override
    public boolean hasMessages(int what) { return getHandler().hasMessages( what ); }
    @Override
    public boolean hasMessages(int what, @Nullable Object object) {
        return getHandler().hasMessages( what, object );
    }

    @Override
    public void removeMessages(int what) {
        getHandler().removeMessages( what );
    }
    @Override
    public void removeMessages(int what, @Nullable Object object) {
        getHandler().removeMessages( what, object );
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    public boolean hasCallbacks(@NonNull Runnable r) { return getHandler().hasCallbacks( r ); }

    @Override
    public void removeCallbacks(@NonNull Runnable r) { getHandler().removeCallbacks( r ); }
    @Override
    public void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
        getHandler().removeCallbacks( r, token );
    }

    @Override
    public void removeCallbacksAndMessages(@Nullable Object token) {
        getHandler().removeCallbacksAndMessages( token );
    }

    @NonNull
    @Override
    public Message obtainMessage() { return getHandler().obtainMessage(); }
    @NonNull
    @Override
    public Message obtainMessage(int what) { return getHandler().obtainMessage( what ); }
    @NonNull
    @Override
    public Message obtainMessage(int what, @Nullable Object obj) {
        return getHandler().obtainMessage( what, obj );
    }
    @NonNull
    @Override
    public Message obtainMessage(int what, int arg1, int arg2) {
        return getHandler().obtainMessage( what, arg1, arg2 );
    }
    @NonNull
    @Override
    public Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
        return getHandler().obtainMessage( what, arg1, arg2, obj );
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public android.os.Handler createAsyncForOs(@NonNull Looper looper) {
        return android.os.Handler.createAsync( looper );
    }
    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public android.os.Handler createAsyncForOs(@NonNull Looper looper,
                                               @NonNull android.os.Handler.Callback callback) {
        return android.os.Handler.createAsync( looper, callback );
    }

    @Override
    public void dump(@NonNull Printer pw, @NonNull String prefix) { getHandler().dump( pw, prefix ); }

    @NonNull
    @Override
    public Looper getLooper() { return getHandler().getLooper(); }

    @Override
    public boolean handleMessage(@NonNull Message msg) { return false; }
    @Override
    public void dispatchMessage(@NonNull Message msg) {}
}